﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Sgr.Apis;
using Sgr.AspNetCore;
using Sgr.Data;
using Sgr.Domain.Uow;
using Sgr.EntityFrameworkCore;
using Sgr.Generator;
using Sgr.MediatR.Behaviors;
using Sgr.Utilities;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Data.Common;

namespace Sgr.Admin.WebHost
{
    /// <summary>
    /// 应用程序入口类
    /// <remarks>
    ///
    /// 集成开发时启动后端API服务
    ///
    ///     cd src/Apps/Sgr.Admin.WebHost
    ///     dotnet run --environment Development --urls="https://localhost:5000"
    ///
    /// </remarks>
    /// </summary>
    public class Program
    {
        /// <summary>
        /// 应用程序入口点
        /// </summary>
        /// <param name="args">启动参数</param>
        public static async Task Main(string[] args)
        {
            // 确保日志目录存在
            EnsureLogDirectoriesExist();

            // 创建Web应用程序构建器
            var builder = WebApplication.CreateBuilder(args);

            // 配置应用程序配置源
            ConfigureAppConfiguration(builder);

            // 配置日志系统
            ConfigureLogging(builder);

            // 配置数据库服务
            ConfigureDatabase(builder);

            // 配置Web服务
            ConfigureServices(builder);

            // 配置Swagger文档
            ConfigureSwagger(builder);

            // 构建应用程序
            var app = builder.Build();

            // 配置中间件管道
            ConfigureMiddleware(app);

            // 开发环境下初始化数据库
            // 检查是否包含初始化数据库参数
            bool shouldInitDb = args.Contains("--init-db", StringComparer.OrdinalIgnoreCase);
            await InitializeDatabaseAsync(app, shouldInitDb);

            // 启用所有后台周期性任务
            await StartBackgroundTasksAsync(app);

            // 输出访问地址信息
            LogApplicationUrls(app);

            // 运行应用程序
            await app.RunAsync();
        }

        /// <summary>
        /// 确保日志目录存在
        /// </summary>
        private static void EnsureLogDirectoriesExist()
        {
            try
            {
                var baseDir = AppContext.BaseDirectory;
                var logDirectory = Path.Combine(baseDir, "logs");
                var archiveDirectory = Path.Combine(logDirectory, "archives");

                Directory.CreateDirectory(logDirectory);     // CreateDirectory 如果目录已存在则不会抛出异常
                Directory.CreateDirectory(archiveDirectory);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Failed to create log directories: {ex.Message}");
                throw; // 重新抛出异常，因为日志目录是关键功能
            }
        }

        /// <summary>
        /// 配置应用程序配置源
        /// </summary>
        /// <param name="builder">Web应用程序构建器</param>
        private static void ConfigureAppConfiguration(WebApplicationBuilder builder)
        {
            builder.Configuration
                .SetBasePath(builder.Environment.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
                .AddJsonFile("Config/sgr_database.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"Config/sgr_database.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
                .AddJsonFile("Config/sgr_security.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"Config/sgr_security.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
                .AddJsonFile("Config/sgr_cache.json", optional: false, reloadOnChange: true)
                .AddJsonFile("Config/sgr_modules.json", optional: false, reloadOnChange: true)
                .AddJsonFile("Config/thirdparty.json", optional: false, reloadOnChange: true)
                .AddEnvironmentVariables();

            // 开发环境下添加用户机密配置
            if (builder.Environment.IsDevelopment())
            {
                // 使用的前提条件
                // 1. 在项目中右键点击，选择“管理用户机密”选项
                // 2. 在打开的 secrets.json 文件中添加配置

                builder.Configuration.AddUserSecrets<Program>();
            }
        }

        /// <summary>
        /// 配置日志系统
        /// </summary>
        private static void ConfigureLogging(WebApplicationBuilder builder)
        {
            try
            {
                // 清除默认提供程序
                builder.Logging.ClearProviders();

                // 从配置中获取日志设置
                var loggingConfig = builder.Configuration.GetSection("Logging");
                if (loggingConfig.Exists())
                {
                    // 应用配置文件中的日志设置
                    builder.Logging.AddConfiguration(loggingConfig);
                }
                else
                {
                    // 如果没有配置，则使用默认的日志级别
                    builder.Logging.SetMinimumLevel(
                        builder.Environment.IsDevelopment()
                            ? LogLevel.Debug
                            : LogLevel.Warning);
                }

                // 使用NLog
                builder.Host.UseNLogWeb(options =>
                {
                    // 设置NLog内部日志
                    NLog.LogManager.ThrowExceptions = builder.Environment.IsDevelopment();
                    NLog.LogManager.ThrowConfigExceptions = builder.Environment.IsDevelopment();

                    // 记录NLog配置信息
                    var logger = NLog.LogManager.GetCurrentClassLogger();
                    logger.Info($"Environment: {builder.Environment.EnvironmentName}");
                    logger.Info($"ContentRoot: {builder.Environment.ContentRootPath}");
                });
            }
            catch (Exception ex)
            {
                // 如果日志配置失败，至少要将错误写入控制台
                Console.Error.WriteLine($"Failed to configure logging: {ex.Message}");
                Console.Error.WriteLine(ex.StackTrace);

                // 在开发环境中重新抛出异常
                if (builder.Environment.IsDevelopment())
                    throw;

                // 在生产环境中尝试配置基本的控制台日志
                try
                {
                    builder.Logging.ClearProviders();
                    builder.Logging.AddConsole();
                    builder.Logging.SetMinimumLevel(LogLevel.Warning);
                }
                catch
                {
                    // 如果连基本的控制台日志都无法配置，只能忽略错误继续运行
                    Console.Error.WriteLine("Failed to configure fallback console logging.");
                }
            }
        }

        /// <summary>
        /// 配置数据库服务
        /// </summary>
        private static void ConfigureDatabase(WebApplicationBuilder builder)
        {
            builder.Services.AddSgrEntityFrameworkCore();

#if DB_MYSQL
            builder.Services.AddSgrMysqlDbProvider();
#endif

#if DB_SQLITE
            builder.Services.AddSgrSqliteDbProvider();
#endif
            builder.Services.AddSgrDbContext<SgrDbContext, SgrReadOnlyDbContext>();
        }

        /// <summary>
        /// 配置Web服务
        /// </summary>
        private static void ConfigureServices(WebApplicationBuilder builder)
        {
            // 添加基础Web服务
            builder.AddSgrWebBuilder();

            // 配置幂等、集成消息、编码服务
            builder.Services.UseEventBusRelayBoxs<SgrDbContext>(builder.Configuration);
            builder.Services.UseMessageTrackerLogs<SgrDbContext>();
            builder.Services.UseBillNumbering<SgrDbContext>(builder.Configuration);

            // 配置MediatR
            builder.Services.AddMediatR(cfg =>
            {
                cfg.RegisterServicesFromAssemblyContaining<Program>();
#if DEBUG
                cfg.AddOpenBehavior(typeof(LoggingBehavior<,>));
#endif
                cfg.AddOpenBehavior(typeof(ValidatorBehavior<,>));
                cfg.AddOpenBehavior(typeof(TransactionBehavior<,>));
            });

            // 配置Minimal API 和 ProblemDetails 的JSON序列化
            builder.Services.ConfigureHttpJsonOptions(options =>
            {
                JsonHelper.UpdateJsonSerializerOptions(options.SerializerOptions);
            });

            // 配置控制器和JSON序列化
            builder.Services.AddControllers()
                .AddJsonOptions(options => JsonHelper.UpdateJsonSerializerOptions(options.JsonSerializerOptions));

            // 配置认证
            builder.Services.AddSgrAuthentication(builder.Configuration, true);
            builder.Services.AddEndpointsApiExplorer();

            //配置跨域
            builder.ConfigureCors();

            ////配置压缩
            //builder.ConfigureCompression();
        }

        /// <summary>
        /// 配置Swagger文档
        /// </summary>
        private static void ConfigureSwagger(WebApplicationBuilder builder)
        {
            var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml", SearchOption.AllDirectories);
            builder.Services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("sgr", new OpenApiInfo
                {
                    Version = "v1",
                    Title = "SGR API",
                    Description = "An ASP.NET Core Web API for SGR",
                    TermsOfService = new Uri("https://gitee.com/maplehub/sgr-core"),//https://github.com/fengqinhua/sgr
                    Contact = new OpenApiContact { Name = "Mapleleaf", Email = "fengqinhua2016@163.com" },
                    License = new OpenApiLicense { Name = "MIT License", Url = new Uri("https://gitee.com/maplehub/sgr-core/blob/master/LICENSE") }
                });

                options.AddSgrAuthenticationSupport();
                // 确保没有禁用可空类型支持
                options.SupportNonNullableReferenceTypes(); // 确保启用（默认已启用）

                foreach (var xmlFile in xmlFiles)
                {
                    if (Path.GetFileName(xmlFile).StartsWith("Sgr"))
                        options.IncludeXmlComments(xmlFile);
                }

                //options.OperationFilter<EnumOperationFilter>();
                options.SchemaFilter<EnumWithNamesSchemaFilter>();  //支持枚举
                                                                    //options.SchemaFilter<NullableSchemaFilter>();       //支持Nullable
                                                                    //options.MapType<string?>(() => new OpenApiSchema { Type = "string", Nullable = true });
                                                                    //options.MapType<int?>(() => new OpenApiSchema { Type = "integer", Nullable = true });
            });
        }

        /// <summary>
        /// 配置中间件管道
        /// </summary>
        private static void ConfigureMiddleware(WebApplication app)
        {
            var logger = app.Services.GetRequiredService<ILogger<Program>>();

            //// 启用压缩中间件件
            //var settings = app.Configuration
            //    .GetSection("Compression")
            //    .Get<CompressionSettings>();
            //if (settings?.EnableCompression ?? false)
            //{
            //    // 使用响应压缩
            //    app.UseResponseCompression();
            //}

            if (!app.Environment.IsDevelopment())
            {
                //app.UseExceptionHandler("/Error");
                // HSTS (HTTP Strict Transport Security) 是一个安全策略机制
                // -> 通过添加特殊的响应头，告诉浏览器在指定时间内只能通过 HTTPS 访问该网站
                // -> 这样可以防止中间人攻击，保护用户的隐私
                // -> 这是一个长期的安全策略，一旦设置，在指定时间内浏览器将直接使用 HTTPS
                // -> 主要用于生产环境，防止 HTTP 降级攻击
                logger.LogInformation("Enabling HSTS security policy");
                app.UseHsts();
            }

            logger.LogInformation("Enabling HTTPS redirection");
            app.UseHttpsRedirection();          // 对所有环境启用 HTTPS 重定向

            app.UseStaticFiles();               // 静态文件中间件

            // 启用跨域中间件（根据配置决定是否启用）
            // • 开发环境：启用CORS以方便前端开发调试
            // • 生产环境：默认禁用CORS, 如需启用，严格限制允许的来源,只允许必要的HTTP方法和请求头
            var corsConfig = app.Configuration.GetSection("Cors").Get<CorsSettings>();
            if (corsConfig?.EnableCors == true)
            {
                logger.LogInformation("Enabling CORS support");
                logger.LogDebug("CORS configuration: {CorsConfig:j}", corsConfig);

                app.UseCors("DefaultCorsPolicy");
            }

            //  自定义Web中间件
            app.UseSgrWeb(opts =>
            {
                opts.EnableRequestRouteMiddleware = false;                                          // 启用RequestRoute中间件
                opts.EnableSecurityHeadersMiddleware = true;                                        // 启用PoweredBy中间件
                opts.EnableAuditLogMiddleware = false;                                              // 启用审计日志中间件
                opts.EnableExceptionHandler = true;                                                 // 启用异常处理中间件
                opts.EnableRateLimiter = !app.Environment.IsDevelopment();                          // 启用限速中间件
                opts.ExceptionHandlingOptionsConfigure = options =>
                {
                    // 是否包含详细信息
                    options.IncludeFullDetails = app.Environment.IsDevelopment();
                };

                // 记录安全中间件配置
                logger.LogInformation("Security middleware configuration:");
                logger.LogInformation("  - Request route: {enabled}", opts.EnableRequestRouteMiddleware);
                logger.LogInformation("  - Security headers: {enabled}", opts.EnableSecurityHeadersMiddleware);
                logger.LogInformation("  - Audit logging: {enabled}", opts.EnableAuditLogMiddleware);
                logger.LogInformation("  - Exception handling: {enabled}", opts.EnableExceptionHandler);
                logger.LogInformation("  - Rate limiting: {enabled}", opts.EnableRateLimiter);
            });

            // 开发环境下启用Swagger
            if (app.Environment.IsDevelopment())
            {
                ConfigureOpenApiUI(app);
            }

            //// 启用路由中间件
            //app.UseRouting();

            // 安全中间件
            logger.LogInformation("Enabling authentication middleware");
            app.UseAuthentication();            // 先进行身份认证

            logger.LogInformation("Enabling authorization middleware");
            app.UseAuthorization();             // 再进行授权验证

            // 最后配置路由映射
            app.MapControllers();

            // 添加模块中的路由
            app.MapSgrModuleEndpoints();

            app.MapGroup("/api/system")
                .MapSystemInfoEndpoints()
                .WithTags("System");

            app.MapGroup("/api/times")
               .MapTimesEndpoints()
               .WithTags("Times");

            app.MapValuesEndpoints();
        }

        /// <summary>
        /// 配置OpenApiUI
        /// </summary>
        private static void ConfigureOpenApiUI(WebApplication app)
        {
            app.UseSwagger(options =>
            {
                options.RouteTemplate = "docs/{documentName}/swagger.json";
                options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0;
            });

            app.UseSwaggerUI(options =>
            {
                options.SwaggerEndpoint("sgr/swagger.json", "SGR API");
                options.RoutePrefix = "docs";
            });
        }

        /// <summary>
        /// 初始化数据库
        /// </summary>
        private static async Task InitializeDatabaseAsync(WebApplication app, bool shouldInitDb)
        {
            using var scope = app.Services.CreateScope();

            if (shouldInitDb)
            {
                var unitOfWorkManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
                var databaseSeed = scope.ServiceProvider.GetRequiredService<IDatabaseSeed>();

                using var uow = unitOfWorkManager.Begin(new UnitOfWorkOption());

                await databaseSeed.SeedAsync();
                await uow.CommitAsync();
            }
            else
            {
                // 生产环境下，预热数据
                var segmentNumIdGenerator = scope.ServiceProvider.GetRequiredService<ISegmentNumIdGenerator>();
                await segmentNumIdGenerator.InitAsync();
            }
        }

        /// <summary>
        /// 启动后台任务
        /// </summary>
        /// <param name="app">应用程序实例</param>
        private static async Task StartBackgroundTasksAsync(WebApplication app)
        {
            var logger = app.Services.GetRequiredService<ILogger<Program>>();

            try
            {
                // 检查是否启用后台任务
                var enableBackgroundTasks = app.Configuration.GetValue<bool>("Sgr:BackgroundTasks:AutoStart", true);
                if (!enableBackgroundTasks)
                {
                    logger.LogInformation("后台任务自动启动已禁用");
                    return;
                }

                logger.LogInformation("正在启动系统中的所有后台周期性任务...");

                // 短暂延迟，确保所有服务完全就绪
                await Task.Delay(50);

                app.ReStartAllRecurringTasks();

                logger.LogInformation("后台周期性任务启动完成");
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "启动后台周期性任务时发生错误，但应用程序将继续运行");
                // 不重新抛出异常，避免影响应用程序启动
            }
        }

        /// <summary>
        /// 输出应用程序访问地址信息
        /// </summary>
        private static void LogApplicationUrls(WebApplication app)
        {
            var logger = app.Services.GetRequiredService<ILogger<Program>>();
            var isDevelopment = app.Environment.IsDevelopment();

            logger.LogInformation("");
            logger.LogInformation("=================================================================");
            logger.LogInformation($"Application startup complete - {app.Environment.EnvironmentName}");

            var urls = app.Urls.ToList();
            if (urls.Count > 0)
            {
                logger.LogInformation("-----------------------------------------------------------------");

                foreach (var url in app.Urls)
                {
                    logger.LogInformation($"API Service: {url}");

                    if (isDevelopment)
                    {
                        logger.LogInformation($"API Documentation: {url}/docs");
                        //logger.LogInformation($"健康检查: {url}/health");
                    }
                }
            }

            var corsConfig = app.Configuration.GetSection("Cors").Get<CorsSettings>();
            if (corsConfig?.EnableCors == true)
            {
                logger.LogInformation("-----------------------------------------------------------------");
                logger.LogInformation("CORS enabled, allowed origins:");
                foreach (var origin in corsConfig.AllowedOrigins)
                {
                    logger.LogInformation($"  • {origin}");
                }
            }
            else
            {
                logger.LogInformation("-----------------------------------------------------------------");
                logger.LogInformation("CORS support is disabled");
            }

            logger.LogInformation("=================================================================");
            logger.LogInformation("");

            if (!app.Environment.IsDevelopment())
            {
                Console.WriteLine("app is running ...");
            }
        }
    }
}